﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Text.RegularExpressions;

namespace CMSApplication.Models
{
	public class FullTextSearch
	{
		/// <summary>
		/// Simple search function searches for the string as provided.
		/// </summary>
		/// <param name="data">Takes IQueryable of type Content.</param>
		/// <param name="search">Take a search string.</param>
		/// <returns>Returns an IQueryable of type Content.</returns>
		public IQueryable<Content> SearchSIMPLE(IQueryable<Content> data, string search)
		{
			data = from c in data
				   where c.content_markdown.Contains(search)
				   select c;

			return data;
		}

		/// <summary>
		/// AND seach takes all space delimited terms in a search string and performs an AND search.
		/// </summary>
		/// <param name="data">Takes IQueryable of type Content.</param>
		/// <param name="search">Take a search string.</param>
		/// <returns>Returns an IQueryable of type Content.</returns>
		public IQueryable<Content> SearchAND(IQueryable<Content> data, string search)
		{
			var terms = tokenizeParam(search);

			foreach (var term in terms)
			{
				data = from c in data
					   where c.content_markdown.Contains(term)
					   select c;
			}

			return data;
		}

		/// <summary>
		/// OR search type A, uses an expand/reduce algorithm to provide ORed search results.
		/// </summary>
		/// <param name="data">Takes IQueryable of type Content.</param>
		/// <param name="search">Take a search string.</param>
		/// <returns>Returns an IQueryable of type Content.</returns>
		public IQueryable<Content> SearchORa(IQueryable<Content> data, string search)
		{
			var terms = tokenizeParam(search);

			var numOfTerms = terms.Count();

			List<IQueryable<Content>> queries = new List<IQueryable<Content>>();

			//find
			foreach (var term in terms)
			{
				queries.Add(
								from c in data
								where c.content_markdown.Contains(term)
								select c
						   );
			}

			//merge
			for (int i = queries.Count(); i > 0; i--)
			{
				queries[0] = queries[0].Concat(queries[i]);
			}

			//dedupe
			var query = (
							from c in queries[0]
							select c
						).Distinct();

			return query;
		}

		/// <summary>
		/// OR search type B, uses an inversed AND algorithm to provide ORed search results.
		/// </summary>
		/// <param name="data">Takes IQueryable of type Content.</param>
		/// <param name="search">Take a search string.</param>
		/// <returns>Returns an IQueryable of type Content.</returns>
		public IQueryable<Content> SearchORb(IQueryable<Content> data, string search)
		{
			var terms = tokenizeParam(search);

			var query = from c in data
						select c;

			//get everything not in the list
			foreach (var term in terms)
			{
				query = from c in query
						where !c.content_markdown.Contains(term)
						select c;
			}

			//invert the list
			query = from c in data
					where !(
								from q in query
								select q.content_id
							).Contains(c.content_id)
					select c;

			return query;
		}

		/// <summary>
		/// Takes a string and returns a space delimited list of strings.
		/// </summary>
		/// <param name="search">Takes a space delimited string.</param>
		/// <returns>Returns a list of strings.</returns>
		private List<string> tokenizeParam(string search)
		{
			var terms = Regex.Matches(search, @"([^ ]+)", RegexOptions.None);

			var returnTerms = new List<string>();

			foreach (var term in terms)
			{
				returnTerms.Add(term.ToString());
			}

			return returnTerms;
		}
	}
}